package com.smsc.headend.task.engine.executor.impl.deviceconfig;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dto.AnsiSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.protocol.ansi.AnsiByteBuffer;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.DeviceActionType;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.deviceconfig.SetProfileObjPrdHandler;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 读取Profile CaptureObj Period
 */
@Slf4j
public class SetProfileObjPrdExecutor implements TaskExecutor {
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;

    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;

    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    AssetService assetService;
    @Autowired
    TaskDAO taskDAO;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
//        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());
        Meter meter = getMeter(task, assetService);
        MeterConfigDMO meterConfigDMO = task.getMeterId() == null ? null : assetService.getMeterConfigById(task.getMeterId());
        Map<String, Object> paramMap = getParameterMap(task, atomicTask);
        Long profileSubDataItemId = getProfileSubDataItemId(paramMap, atomicTask);
        //曲线cosem
        List<DataItemDTO> dataItemCosemList = dataItemFacade.getDataItemsById(meter.getModelId(), DeviceActionType.Set, profileSubDataItemId);
        if (CollectionUtils.isEmpty(dataItemCosemList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }
        DeviceOperation operationGroup = assetService.getDictionary(atomicTask.getGroupId(), DictionaryType.OperationGroup.getType());
        byte[][] packets = getPacketByOperationGroup(task, atomicTask, meterConfigDMO, operationGroup, dataItemCosemList, meter, paramMap);
        log.info("packets {}", ByteUtils.byteToHexString(packets[0]));
        if(StrUtil.equalsAny(task.getTaskType(), TaskType.ScheduleSet_LP_Period_OBJS.name(), TaskType.ScheduleSet_LB_Capture_OBJS.name())) {
            connectorService.sendPacket(task, atomicTask, dataItemCosemList.get(0).getRoleId(), true, packets);
        } else {
            connectorService.sendPacket(task, atomicTask, dataItemCosemList.get(0).getRoleId(), task.getIsODR(), packets);
        }
        Class handlerClass = loadProfileReadHandler(atomicTask);
        return createAtomicState(handlerClass);
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.Action_DeviceParamConfig_Set.name())) {
            OperationName operationName = OperationName.forId(atomicTask.getOperationId());
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), operationName.getName(), message, 600L, false);
        }
        return true;
    }

    private byte[][] getPacketByOperationGroup(Task task, AtomicTask atomicTask, MeterConfigDMO meterConfigDMO, DeviceOperation operationGroup, List<DataItemDTO> dataItemCosemList, Meter meter, Map<String, Object> paramMap) throws UDISTaskExecutorException {
        byte[][] packet = null;
        OperationGroup operationGp = OperationGroup.forId(operationGroup.getOptId().intValue());
        switch (operationGp) {
            case SetMeterMeasProfileConfig:
            case SetDisplay:
                packet = loadProfilePacket(task, atomicTask,meterConfigDMO, meter, operationGroup, dataItemCosemList, paramMap);
                break;
            default:
                log.info("no implementation of this type {}", operationGp);
        }
        return packet;
    }

    private byte[][] loadProfilePacket(Task task, AtomicTask atomicTask, MeterConfigDMO meterConfigDMO, Meter meter, DeviceOperation operationGroup, List<DataItemDTO> dataItemCosemList, Map<String, Object> paramMap) throws UDISTaskExecutorException {
        byte[][] packet = null;
        OperationName opName = OperationName.forId(atomicTask.getOperationId());
        switch (opName) {
            case SetProfilePeriod:
                Object value = getLpPeriodByOperationGroup(operationGroup, task, paramMap);
                log.info("Set loadPorfile Period {}", value);
                DataItemDTO dataItemDTO = dataItemCosemList.get(0);
                // ansi 需要组装 lp setting
                if(dataItemDTO instanceof AnsiSubDataItemDTO) {
                    int second = Convert.toInt(value);
                    int MAX_INT_TIME_SET1 = second / 60;
                    int NBR_BLK_INTS_SET1 = 1440 / MAX_INT_TIME_SET1;
                    Long lpTemplateId = Long.valueOf(String.valueOf(paramMap.get("lpTemplateId")));
                    List<DataItemDTO> dataItemDTOS = assetManagementRemoteService.getProfileDataItemForDevice(task.getMeterId(), lpTemplateId);
                    int NBR_CHNS_SET1 = dataItemDTOS.size();
                    AnsiByteBuffer ansiByteBuffer = new AnsiByteBuffer(6);
                    ansiByteBuffer.setUInt16(120);
                    ansiByteBuffer.setUInt16(NBR_BLK_INTS_SET1);
                    ansiByteBuffer.setUInt8(NBR_CHNS_SET1);
                    ansiByteBuffer.setUInt8(MAX_INT_TIME_SET1);
                    value = ansiByteBuffer.getData();
                }
                packet = new byte[][]{packetService.writeSimpleDataPacket(dataItemCosemList, meter, value, meterConfigDMO)};
                break;
            case SetProfileCaptureObjects:
                Long templateId;
                List<DataItemDTO> dataItemDTOS = null;
                if (!Objects.isNull(paramMap.get("lpTemplateId"))) {
                    templateId = Long.valueOf(String.valueOf(paramMap.get("lpTemplateId")));
                    dataItemDTOS = assetManagementRemoteService.getProfileDataItemForDevice(task.getMeterId(), templateId);
                    if (CollUtil.isEmpty(dataItemDTOS)) {
                        throw new UDISTaskExecutorException(TaskExecuteErrorCode.NO_CAPTURE_METER.toString());
                    }
                } else if (!Objects.isNull(paramMap.get("lbTemplateId"))) {
                    templateId = Long.valueOf(String.valueOf(paramMap.get("lbTemplateId")));
                    dataItemDTOS = assetManagementRemoteService.getEventProfileDataItemForDevice(task.getMeterId(), templateId, DeviceType.METER.getId());
                    if (CollUtil.isEmpty(dataItemDTOS)) {
                        throw new UDISTaskExecutorException(TaskExecuteErrorCode.NO_EVENT_METER.toString());
                    }
                } else if (!Objects.isNull(paramMap.get("displayTemplateId"))) {
                    templateId = Long.valueOf(String.valueOf(paramMap.get("displayTemplateId")));
                    dataItemDTOS = assetManagementRemoteService.getDisplayProfileDataItemForDevice(task.getMeterId(), templateId);
                    if (CollUtil.isEmpty(dataItemDTOS)) {
                        throw new UDISTaskExecutorException(TaskExecuteErrorCode.NO_EVENT_METER.toString());
                    }
                }
                packet = packetService.writeProfileCaptureObjects(dataItemCosemList, meter, dataItemDTOS, meterConfigDMO);
                break;
            case AnsiStopLp:
            case AnsiClearData:
            case AnsiStartLp:
                packet = packetService.getProcedurePacket(dataItemCosemList.get(0), meter);
                break;
        }
        return packet;
    }

    private Object getLpPeriodByOperationGroup(DeviceOperation operationGroup, Task task, Map<String, Object> paramMap) {
        if (operationGroup.getOptName().equalsIgnoreCase(OperationGroup.SetMeterMeasProfileConfig.toString())) {
            Long lpTemplateId = Long.valueOf(String.valueOf(paramMap.get("lpTemplateId")));
            return assetManagementRemoteService.getProfileIntervalTime(task.getMeterId(), lpTemplateId);
        }
        return null;
    }

    private Long getProfileSubDataItemId(Map<String, Object> paramMap, AtomicTask atomicTask) {
        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        switch (operationName) {
            case SetProfilePeriod:
                return Long.valueOf((paramMap.get("dataItemId") + "104")); // 104是捕获周期属性
            case SetProfileCaptureObjects:
                return Long.valueOf((paramMap.get("dataItemId") + "103")); // 103是7类 Object 捕获对象属性
            case AnsiStopLp:
                return Long.valueOf((paramMap.get("dataItemId") + "109"));
            case AnsiClearData:
                return Long.valueOf((paramMap.get("dataItemId") + "110"));
            case AnsiStartLp:
                return Long.valueOf((paramMap.get("dataItemId") + "111"));
        }
        return null;
    }

    private Class loadProfileReadHandler(AtomicTask atomicTask) {
        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        switch (operationName) {
            case SetProfilePeriod:
            case SetProfileCaptureObjects:
            case AnsiStopLp:
            case AnsiClearData:
            case AnsiStartLp:
                return SetProfileObjPrdHandler.class;
            default:
                return null;
        }
    }
}
